3. Estructura de un programa WinAPI

3. Estructura de un programa WinAPI

Estructura basada en mensajes de windows, hace más tareas que un programa no WinAPI.

#include <windows.h> // Obligatoria para WinAPI

// Prototipos:
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);

// Función de entrada:
int WINAPI WinMain(HINSTANCE hInstance, 
                   HINSTANCE hPrevInstance,
                   LPSTR lpszCmdParam, 
                   int nCmdShow)
{
   // Declaración:
   // Inicialización:
   // Bucle de mensajes:
   return Message.wParam;
}
#include <windows.h> // Obligatoria para WinAPI

// Prototipos:
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);

// Función de entrada:
int WINAPI WinMain(HINSTANCE hInstance, 
                   HINSTANCE hPrevInstance,
                   LPSTR lpszCmdParam, 
                   int nCmdShow)
{
   // Declaración:
   // Inicialización:
   // Bucle de mensajes:
   return Message.wParam;
}

Prototipos:

Cada tipo (o clase) de ventana que usemos en nuestro programa (normalmente sólo será una), o cada cuadro de diálogo (de estos puede haber muchos), necesitará un procedimiento propio, que deberemos declarar y definir. Siguiendo la estructura de un programa C, esta es la zona normal de declaración de prototipos.

3.1. Entrada: WinMain

La función de entrada de un programa Windows es WinMain, en vez de main. Normalmente, la definición de esta función cambia muy poco de una aplicaciones a otras. Se divide en tres partes claramente diferenciadas: Declaración, inicialización y bucle de mensajes.

3.1a. Declaración de la función y cuatro parámetros de entrada

3.1b. Declaración de las variables, como mínimo tres

3.2. Inicialización

Esta zona se encarga de registrar la clase o clases de ventana, crear la ventana y visualizarla en pantalla.

Para registrar la clase primero hay que rellenar adecuadamente la estructura WNDCLASSEX, que define algunas características que serán comunes a todas las ventanas de una misma clase, como color de fondo, icono, menú por defecto, el procedimiento de ventana, etc. Después hay que llamar a la función RegisterClassEx.

En el caso de usar una estructura WNDCLASS se debe registrar la clase usando la función RegisterClass.

A continuación se crea la ventana usando la función CreateWindowEx, la función CreateWindow ha caído prácticamente en desuso. Cualquiera de estas dos funciones nos devuelve un manipulador de ventana que podemos necesitar en otras funciones, sin ir más lejos, la siguiente.

Para que la ventana sea visible en pantalla hay que llamar a la función ShowWindow. La primera vez que se llama a ésta función, después de crear la ventana, se puede usar el parámetro nCmdShow de WinMain como parámetro o mejor aún, como se recomienda por Windows, el valor SW_SHOWDEFAULT.

3.3. Bucle de mensajes

Este es el núcleo de la aplicación, el programa permanece en este bucle mientras la función GetMessage retorne con un valor TRUE. Aunque puede retornar TRUE, FALSE o -1 (error).

/*while(GetMessage(&mensaje, 0, 0, 0)) ver NOTA   */
while(TRUE == GetMessage(&mensaje, 0, 0, 0))   /* optimizado */
{
   TranslateMessage(&mensaje); 
   DispatchMessage(&mensaje); 
}
/*while(GetMessage(&mensaje, 0, 0, 0)) ver NOTA   */
while(TRUE == GetMessage(&mensaje, 0, 0, 0))   /* optimizado */
{
   TranslateMessage(&mensaje); 
   DispatchMessage(&mensaje); 
}

NOTA: El código que se suele usar es el que no está comentado, el problema con es que si GetMessage regresa un valor -1, que indica un error, la condición del “while” se considera verdadera, y el bucle continúa. Si el error es permanente, el programa jamás terminará.

La función TranslateMessage se usa para traducir los mensajes de teclas virtuales a mensajes de carácter. Veremos esto con más detalle en el capítulo dedicado al teclado (cap. 34).

Los mensajes traducidos se reenvían a la lista de mensajes del proceso, y se recuperarán con las siguientes llamadas a GetMessage.

La función DispatchMessage envía el mensaje al procedimiento de ventana, donde será tratado adecuadamente. El próximo capítulo está dedicado al procedimiento de ventana, y al final de él estaremos en disposición de crear nuestro primer programa Windows.

3.4. Código

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
   LPSTR lpszCmdParam, int nCmdShow)
{
   /* Declaración: */
   HWND hwnd;
   MSG mensaje;
   WNDCLASSEX wincl;

   /* Inicialización: */
   /* Estructura de la ventana */
   wincl.hInstance = hInstance;
   wincl.lpszClassName = "NUESTRA_CLASE";
   wincl.lpfnWndProc = WindowProcedure;
   wincl.style = CS_DBLCLKS;
   wincl.cbSize = sizeof(WNDCLASSEX);

   /* Usar icono y puntero por defecto */
   wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
   wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
   wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
   wincl.lpszMenuName = NULL;
   wincl.cbClsExtra = 0;
   wincl.cbWndExtra = 0;
   wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND;

   /* Registrar la clase de ventana, si falla, salir del programa */
   if(!RegisterClassEx(&wincl)) return 0;

   hwnd = CreateWindowEx(
           0,
           "NUESTRA_CLASE",
           "Ejemplo 001",
           WS_OVERLAPPEDWINDOW,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           544,
           375,
           HWND_DESKTOP,
           NULL,
           hThisInstance,
           NULL
   );

   ShowWindow(hwnd, SW_SHOWDEFAULT);

   /* Bucle de mensajes: */
   while(TRUE == GetMessage(&mensaje, 0, 0, 0))
   {
      TranslateMessage(&mensaje);
      DispatchMessage(&mensaje);
   }

   return mensaje.wParam;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
   LPSTR lpszCmdParam, int nCmdShow)
{
   /* Declaración: */
   HWND hwnd;
   MSG mensaje;
   WNDCLASSEX wincl;

   /* Inicialización: */
   /* Estructura de la ventana */
   wincl.hInstance = hInstance;
   wincl.lpszClassName = "NUESTRA_CLASE";
   wincl.lpfnWndProc = WindowProcedure;
   wincl.style = CS_DBLCLKS;
   wincl.cbSize = sizeof(WNDCLASSEX);

   /* Usar icono y puntero por defecto */
   wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
   wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
   wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
   wincl.lpszMenuName = NULL;
   wincl.cbClsExtra = 0;
   wincl.cbWndExtra = 0;
   wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND;

   /* Registrar la clase de ventana, si falla, salir del programa */
   if(!RegisterClassEx(&wincl)) return 0;

   hwnd = CreateWindowEx(
           0,
           "NUESTRA_CLASE",
           "Ejemplo 001",
           WS_OVERLAPPEDWINDOW,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           544,
           375,
           HWND_DESKTOP,
           NULL,
           hThisInstance,
           NULL
   );

   ShowWindow(hwnd, SW_SHOWDEFAULT);

   /* Bucle de mensajes: */
   while(TRUE == GetMessage(&mensaje, 0, 0, 0))
   {
      TranslateMessage(&mensaje);
      DispatchMessage(&mensaje);
   }

   return mensaje.wParam;
}